package com.example.fuluchou.service.impl;

import com.example.fuluchou.common.errorcode.ServiceErrorCodeConstants;
import com.example.fuluchou.common.untils.JacksonUtil;
import com.example.fuluchou.common.untils.RedisUtil;
import com.example.fuluchou.controller.param.DrawPrizeParam;
import com.example.fuluchou.controller.param.ShowWinningRecordsParam;
import com.example.fuluchou.dao.dataobject.*;
import com.example.fuluchou.dao.mapper.*;
import com.example.fuluchou.service.DrawPrizeService;
import com.example.fuluchou.service.dto.WinnerRecordDTO;
import com.example.fuluchou.service.enums.ActivityPrizeStatusEnum;
import com.example.fuluchou.service.enums.ActivityPrizeTiersEnum;
import com.example.fuluchou.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.example.fuluchou.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.example.fuluchou.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {

    private static final Logger logger = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);

    private final Long WINNING_RECORDS_TIMEOUT = 60*60*24*2L;
    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void drawPrize(DrawPrizeParam param) {

        Map<String,String> map = new HashMap<>();
        map.put("messageId",String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        //发消息：交换机、绑定的Key、消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);
        logger.info("mq消息发送成功: map={}",JacksonUtil.writeValueAsString(map));
    }

    @Override
    public boolean checkDrawPrizeParam(DrawPrizeParam param) {

        //获取活动信息
        ActivityDO activityDO = activityMapper.selectByActivityId(param.getActivityId());
        //获取奖品信息，activity_prize 表中获取，后面涉及到奖品数量和人员数量的校验
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper
                .selectByAPId(param.getActivityId(),param.getPrizeId());

        //活动或奖品是否存在
        if(null == activityDO || null == activityPrizeDO){
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            logger.info("校验抽奖请求失败！失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY.getMsg());
            return false;
        }
        //活动是否有效
        if(activityDO.getStatus()
                .equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            //throw new ServiceException((ServiceErrorCodeConstants.ACTIVITY_COMPLETED));
            logger.info("校验抽奖请求失败！失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_COMPLETED.getMsg());
            return false;

        }

        //奖品是否有效
        if(activityPrizeDO.getStatus()
                .equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
            //throw new ServiceException((ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED));
            logger.info("校验抽奖请求失败！失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED.getMsg());
            return false;
        }
        //中奖者人数是否和设置奖品数量一致
        if(activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()){
            //throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            logger.info("校验抽奖请求失败！失败原因:{}",
                    ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR.getMsg());
            return false;
        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        //查询相关信息：活动、人员、奖品、活动关联奖品
        ActivityDO activityDO = activityMapper.selectByActivityId(param.getActivityId());
        List<UserDO> userDOList = userMapper.batchSelectByIds(param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList())
        );
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        //构造中奖者记录，保存
        List<WinningRecordDO> winnerRecordDOList = userDOList.stream()
                .map(userDO->{
                    WinningRecordDO winnerRecordDO = new WinningRecordDO();
                    winnerRecordDO.setActivityId(activityDO.getId());
                    winnerRecordDO.setActivityName(activityDO.getActivityName());
                    winnerRecordDO.setPrizeId(prizeDO.getId());
                    winnerRecordDO.setPrizeName(prizeDO.getName());
                    winnerRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winnerRecordDO.setWinnerId(userDO.getId());
                    winnerRecordDO.setWinnerName(userDO.getUserName());
                    winnerRecordDO.setWinnerEmail(userDO.getEmail());
                    winnerRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winnerRecordDO.setWinningTime(param.getWinningTime());
                    return winnerRecordDO;
                })
                .collect(Collectors.toList());
        winningRecordMapper.batchInsert(winnerRecordDOList);
        // 缓存中奖者记录
        // 1、缓存奖品维度中奖记录(WinningRecord_activityId_prizeId, winningRecordDOList（奖品维度的中奖名单）)
        cacheWinningRecords(param.getActivityId()+"_"+param.getPrizeId(),
                winnerRecordDOList,
                WINNING_RECORDS_TIMEOUT);

        // 2、缓存活动维度中奖记录(WinningRecord_activityId, winningRecordDOList(活动维度的中奖名单))
        // 前提是活动已完成，即处于COMPLETE状态
        if(activityDO.getStatus().
                equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            // 查询活动维度的全量中奖信息
            List<WinningRecordDO> allList = winningRecordMapper.selectByActivityId(param.getActivityId());
            cacheWinningRecords(String.valueOf(param.getActivityId()),
                    allList,
                    WINNING_RECORDS_TIMEOUT);
        }
        return winnerRecordDOList;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if(null == activityId){
            logger.warn("要删除中奖记录的活动ID为空！");
            return;
        }
        //删除数据表
        winningRecordMapper.deleteRecords(activityId,prizeId);

        //删除缓存（奖品维度、活动维度）
        if(null != prizeId){
            deleteWinningRecords(activityId+"_"+prizeId);
        }
        // 无论是否传递了prizeId，都需要删除活动维度的中奖记录缓存：
        // 如果传递了prizeId, 证明奖品未抽奖，必须删除活动维度的缓存记录
        // 如果没有传递prizeId，就只是删除活动维度的信息
        deleteWinningRecords(String.valueOf(activityId));
    }

    @Override
    public List<WinnerRecordDTO> getRecords(ShowWinningRecordsParam param) {
        //查询redis:奖品、活动
        String key = null == param.getPrizeId() ?
                String.valueOf(param.getActivityId()):
                param.getActivityId()+"_"+param.getPrizeId();
        List<WinningRecordDO> winningRecordDOList = getWinningRecords(key);
        if(!CollectionUtils.isEmpty(winningRecordDOList)){
            return convertToWinnerRecordDTOList(winningRecordDOList);
        }

        //如果redis中不存在，查库
        winningRecordDOList = winningRecordMapper.selectByActivityIdOrPrizeId(
                param.getActivityId(),param.getPrizeId());

        //缓存记录到redis中
        if(CollectionUtils.isEmpty(winningRecordDOList)){
            logger.info("查询的中奖记录为空,param:{}",
                    JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }
        cacheWinningRecords(key,winningRecordDOList,WINNING_RECORDS_TIMEOUT);
        return convertToWinnerRecordDTOList(winningRecordDOList);
    }

    private List<WinnerRecordDTO> convertToWinnerRecordDTOList(List<WinningRecordDO> winningRecordDOList) {

        if(CollectionUtils.isEmpty(winningRecordDOList)){
            return Arrays.asList();
        }
        return winningRecordDOList.stream()
                .map(winningRecordDO -> {
                    WinnerRecordDTO result = new WinnerRecordDTO();
                    result.setWinnerId(winningRecordDO.getWinnerId());
                    result.setWinnerName(winningRecordDO.getWinnerName());
                    result.setPrizeName(winningRecordDO.getPrizeName());
                    result.setPrizeTier(
                            ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()));
                    result.setWinningTime(winningRecordDO.getWinningTime());
                    return result;
                })
                .collect(Collectors.toList());
    }


    /**
     * 从缓存中删除中奖记录
     */
    private void deleteWinningRecords(String key) {
        try {
            if(redisUtil.hasKey(WINNING_RECORDS_PREFIX + key)){
                redisUtil.del(WINNING_RECORDS_PREFIX + key);
            }

        }catch (Exception e){
            logger.error("删除缓存中奖记录异常,key:{}",key);
        }

    }

    /**
     * 缓存中奖记录
     *
     * @param key
     * @param winnerRecordDOList
     * @param time
     */
    private void cacheWinningRecords(String key,
                                     List<WinningRecordDO> winnerRecordDOList,
                                     Long time) {
        String str = "";
        try {
            if(!StringUtils.hasText(key)
                    || CollectionUtils.isEmpty(winnerRecordDOList)){
                logger.warn("要缓存的中奖记录为空！ key:{},value:{}",
                        key,
                        JacksonUtil.writeValueAsString(winnerRecordDOList));
                return;
            }
            str = JacksonUtil.writeValueAsString(winnerRecordDOList);
            redisUtil.set(WINNING_RECORDS_PREFIX + key,
                    str,
                    time);
        }catch (Exception e) {
            logger.error("缓存中奖记录异常! key:{},value:{}",WINNING_RECORDS_PREFIX + key,str);
        }

    }

    /**
     * 从缓存中获取中奖记录
     * @param key
     * @return
     */
    private List<WinningRecordDO> getWinningRecords(String key){

        try {
            if(!StringUtils.hasText(key)){
                logger.warn("要从缓存中查询的中奖记录key为空！");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECORDS_PREFIX + key);
            if(!StringUtils.hasText(str)) {
                return Arrays.asList();
            }

            return JacksonUtil.readListValue(str,WinningRecordDO.class);
        }catch (Exception e){
            logger.error("从缓存中查询中奖记录异常！ key:{}",WINNING_RECORDS_PREFIX + key);
            return Arrays.asList();
        }

    }
}
